package dmmm.main;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.GridLayout;
import java.awt.Point;
import java.awt.ScrollPane;
import java.awt.event.*;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.LinkedList;

import javax.swing.*;
import javax.swing.plaf.basic.BasicInternalFrameTitlePane.RestoreAction;
import javax.swing.text.DefaultHighlighter;
import javax.swing.text.Highlighter;

import dmmm.assembler.AssemblerFrame;
import dmmm.assembler.Disassembler;
import dmmm.components.*;
import dmmm.graph.TimeDiagram;
import dmmm.gui.*;
import dmmm.shemes.*;

@SuppressWarnings("serial")
public class Main extends JFrame {
	// Glavna klasa za pokretanje simulatora,sa istanciranjem i pracenjem svih
	// prozora

	private static PrintStream log;// referenca na log file za output
	private String log_name = "log/dmmm/log_debug_"
			+ System.currentTimeMillis() + ".txt";// ime log fajla pri
													// pokretanju aplikacije je
													// svaki put razlicito

	// deklaracije i instanciranja nekih polja
	private GuiScheme currentScheme;// Koja je aktivna trenutna sema na prikazu
									// se kroz ovo polje pamti
	private Pin pc;// referenca na pin koji je izlaz PC registra,koristi se zbog
					// ispisa registra PC
	private Pin mpc;// referenca na pin koji je izlaz mPC registra,koristi se
					// zbog ispisa registra mPC
	private Pin start;// referenca na pin start koji je izlaz FF start,jer ako
						// nije startovan onda su neka dugmad nisu enable-vana
	private ScrollPane scrollPane;// centralni scroll panel i u njega se
									// postavlja GUISchema koja je trenutno na
									// pregledu
	private String[] desc;// opisi faza koje se izvrsavaju,dobijaju se iz
							// StepLoadera kad ucita sve step-ove,pa deskriptori
							// faza su u obliku !aaa!
	private JButton clk_minus = new JButton("CLK-");// dugme za unazad 1 klok
	private JButton clk = new JButton("CLK+");// dugme za napred 1 klok
	private JButton ins_minus = new JButton("INS-");// dugme za unazad 1
													// instrukciju
	private JButton ins = new JButton("INS+");// dugme za napred 1 instrukciju
	private JButton prg = new JButton("PRG+");// dugme za izvrsavnje celog
												// programa
	private JButton gotob = new JButton("GOTO");// dugme za skok na uneti klok
	private JTextField gotot = new JTextField();// polje za unos kloka na koji
												// se zeli skociti
	private JLabel clockLabel = new JLabel("CLK = 0", SwingConstants.CENTER);// Labela
																				// za
																				// ispis
																				// tekuceg
																				// kloka
	private JLabel mpcLabel = new JLabel("mPC = 0", SwingConstants.CENTER);// labela
																			// za
																			// ispis
																			// vrednosti
																			// registra
																			// mPC
	private JLabel pcLabel = new JLabel("PC = 0", SwingConstants.CENTER);// labela
																			// za
																			// ispis
																			// vrednosti
																			// registra
																			// PC
	private JLabel instrLabel = new JLabel("add r5", SwingConstants.CENTER);// labela
																			// za
																			// ispis
																			// trenutne
																			// instrukcije
	private JLabel descLabel = new JLabel("! Čitanje instrukcije !",
			SwingConstants.CENTER);// labela za ispis deskripcije trenutne faze
									// izvrsavnja koja je u obliku !aaa!
	private JTextArea stepsArea = new JTextArea();// teksutalna povrsina gde ce
													// se nalaziti svi stringovi
													// sa opisima svakog step-a
	private JScrollPane scrollSteps;// panel za skrolovanje i tako se moze
									// kretati kroz opis mikroprograma u
									// stepovima

	// ne koristi se,koristilo se kada se ispisivala samo tekuca mikroprogramska
	// instrukcija(1 step) i dodavalo se u east2 panel
	// private JLabel stepLabel = new
	// JLabel("<html>brnotSTART, val00;",SwingConstants.CENTER);//ne koristi se
	// jer east2 panel se ne dodaje,umesto njega se koristi scroll panel za
	// mikroprogram

	private RegistersFrame regsWindow;// referenca na prozor koji prikazuje
										// vrednosti registara
	private MemoryFrame memWindow;// referenca na prozor koji prikazuje sadrzaj
									// memorije
	private TimeDiagram timeDiagram;// referenca na prozor koji prikazuje
									// vremenske dijagrame
	private AssemblerFrame asmWindow;// referenca na prozor gde se unosi
										// asemblerski kod i moze ucitavati
										// program
	// radi cuvanja stanja

	// u ovoj listi se pamti za svaku instrukciju redom na koji klok pocinje,i
	// koristi se kada se stisne dugme INS- da bi se odavde video klok odakle
	// pocinje prethodna instrukcija
	private LinkedList<Integer> instructionsBeginings = new LinkedList<Integer>();

	private JFrame simulationFrame;// referenca na prozor koji poziva ovu klasu,ovo
								// je potrebno samo zbog POTPUNOG zatvaranja
								// simulacije da bi se pogasile sve stvari

	// ne koristi se i izbrisi ovo nakon uvodjenja prozora za asembler
	// private JTextPane asmText;

	//pamtice se trenuci kada je snimljeno stanje simulacije u kriticnim trenucima tj kada su vrsene promene sadrzaja memorije i registara rucno
	private LinkedList<Integer> criticalMoments = new LinkedList<Integer>();
	
	
	public Main(JFrame parent) {
		super("Darko_Diplomski");// naslov simulatora
		setLocation(50, 50);// odakle da se pozicionira simulacioni prozor
		simulationFrame = parent;// zapamtiti referencu na roditeljski prozor

		try {

			try {
				log = new PrintStream(new FileOutputStream(log_name), true);
			} catch (FileNotFoundException e) {
				System.out
						.println("Log file can't be open, logging on standard output");
			}

			initAll();// napravi sve GuiScheme tako sto instancira sve
						// komponente u njima
			connectAll();// poveze sve GuiScheme
			guiAll();// iscrta graficke komponente koje se povezuju u toku tog
						// procesa na pinove komponenata u GUISchemama

			// instanciranje svih mogucih prozora
			memWindow = new MemoryFrame(this);
			// prozor za prikaz registara ima referencu na glavni prozor jer ako
			// se promeni vrednost nekog registra mora da se update glavni
			// prozor za prikaz da se izmenjena vrednost odmah vidi u MAIN
			// prozor
			regsWindow = new RegistersFrame(this);
			timeDiagram = new TimeDiagram();
			asmWindow = new AssemblerFrame();

			// bitno je da bude ispod instancirnaja vremenskog dijagrama zato
			// sto kad se pravi vremenski dijagram
			// on tada poziva metodu koja nazive signala za iscrtavanje povezuje
			// sa pinovima od kojih dobijaju vrednosti
			// da bi se u initialise zapamtile pocetne vrenosti signala onda je
			// potrebno da bude napravljena ta lista signala
			LogicalComponent.initialise();// inicijalizuje simulaciju,sve
											// sekvencijalne komponente izbace
											// svoje pocetne vrednosti i
											// azuriraju kombinacione vrednosti
											// signala
			
			

			// instanciranje klase koja cita mikroprogram iz fajla gde se nalazi
			// mikroprogram
			StepsLoader stepLoader = new StepsLoader(
					"conf/dmmm/microProgram.txt", null);
			desc = stepLoader.getDesc();

			// postavljanje referenci na pinove za pracenje vrednosti koje su
			// potrebne za ispis
			pc = Fetch1.PC();
			mpc = Counter.mPC();
			start = Exec2.START();

			checkInstructionBegining();//provera da se nulti klok stavi za pocetak instrukcija u listu instrukcija
			
			
			// MENU BAR -Instanciranje i dodavanje menija
			makeMenuBar();

			// CENTER --dodacanje centralnog scroll panela sa prikazom seme u
			// centralni deo
			currentScheme = Sistem.getGui();
			setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
			scrollPane = new ScrollPane();
			scrollPane.setMinimumSize(new Dimension(800, 600));
			scrollPane.setSize(new Dimension(800, 600));
			scrollPane.add(currentScheme);
			add("Center", scrollPane);

			// WEST panel: Navigacija i bilo je zamisljeno da se ispod
			// navigacije ispisuje program koji se izvrsava i da se obelezava
			// tekuca instrukcija ali je to izbaceno

			// izbacen deo sa dugmadima(za ucitavanje programa iz
			// fajla,resetovanje sa i bez memorije) kada je napravljen meni
			// JButton loadAsm = new JButton("Ucitaj program");
			// loadAsm.setAlignmentX(CENTER_ALIGNMENT);
			// JButton reset = new JButton("Reset");
			// reset.setAlignmentX(CENTER_ALIGNMENT);
			// JButton resetnomem = new JButton("Reset");
			// resetnomem.setAlignmentX(CENTER_ALIGNMENT);
			// JButton save = new JButton("Sacuvaj sim.");
			// save.setAlignmentX(CENTER_ALIGNMENT);
			// JButton loadSim = new JButton("Ucitaj sim.");
			// loadSim.setAlignmentX(CENTER_ALIGNMENT);

			JPanel west = new JPanel();// pravljenje zapadnog panela
			west.setLayout(new BoxLayout(west, BoxLayout.Y_AXIS));

			Menu menuItems = new Menu(this, null);// instanciranje klase koja
													// sluzi kao
													// navigacija,poveze
													// navigaciju sa semama koje
													// se prikazuju
			JPanel west1 = new JPanel();// gornji deo zapadnog panela koji
										// sadrzi navigaciju
			west1.setLayout(new BoxLayout(west1, BoxLayout.Y_AXIS));
			west1.setBorder(BorderFactory.createTitledBorder("Navigacija"));
			west1.add(menuItems);
			west.add(west1);

			// Izbaceno kada je dodat meni(videti malo gore objasnjeno)
			// JPanel west2 = new JPanel();
			// west2.setLayout(new GridLayout(5, 1, 5, 5));
			// west2.setBorder(BorderFactory.createTitledBorder("Program"));
			// west2.add(loadAsm);
			// west2.add(reset);
			// west2.add(resetnomem);
			// west2.add(save);
			// west2.add(loadSim);
			// west.add(west2);

			add("West", west);

			// EAST panel:prikaz vrednosti kloka,registra PC,mPC,trenutne
			// instrukcije,mikroprograma i dugmici za navigaciju kroz simulaciju

			// statusni deo panela
			clockLabel.setAlignmentX(CENTER_ALIGNMENT);
			mpcLabel.setAlignmentX(CENTER_ALIGNMENT);
			pcLabel.setAlignmentX(CENTER_ALIGNMENT);

			// mikrokod
			instrLabel.setAlignmentX(CENTER_ALIGNMENT);
			descLabel.setAlignmentX(CENTER_ALIGNMENT);
			// stepLabel.setAlignmentX(CENTER_ALIGNMENT);gubi znacaj nakon
			// uvodjenja highlighting micokoda

			// kontrolni deo istocnog panela
			clk_minus.setAlignmentX(CENTER_ALIGNMENT);
			clk.setAlignmentX(CENTER_ALIGNMENT);
			ins_minus.setAlignmentX(CENTER_ALIGNMENT);
			ins.setAlignmentX(CENTER_ALIGNMENT);
			prg.setAlignmentX(CENTER_ALIGNMENT);
			gotob.setAlignmentX(LEFT_ALIGNMENT);
			gotot.setAlignmentX(LEFT_ALIGNMENT);

			// izbacen deo sa dugmadima(za pregled memorije,registara,vremenski
			// dijagam) kada je napravljen meni
			// JButton memory = new JButton("Memorija");
			// memory.setAlignmentX(CENTER_ALIGNMENT);
			// JButton registers = new JButton("Registri");
			// registers.setAlignmentX(CENTER_ALIGNMENT);
			// JButton buttTimeDiagram = new JButton("Vremenski dijagram");
			// buttTimeDiagram.setAlignmentX(CENTER_ALIGNMENT);

			// pravljenje istocnog panela i dodavanje u njega
			JPanel east = new JPanel();
			east.setLayout(new BoxLayout(east, BoxLayout.Y_AXIS));
			east.setPreferredSize(new Dimension(175, 700));

			JPanel east1 = new JPanel();
			east1.setLayout(new GridLayout(3, 1));
			east1.setBorder(BorderFactory.createTitledBorder("Status"));
			east1.add(clockLabel);
			east1.add(mpcLabel);
			east1.add(pcLabel);
			east.add(east1);

			// izbacen deo da se prikazuje samo jedna mirkoinstrukcija
			// JPanel east2 = new JPanel();
			// east2.setLayout(new GridLayout(2, 1));
			// east2.setPreferredSize(new Dimension(175, 100));
			// east2.setBorder(BorderFactory.createTitledBorder("Mikrokod"));
			// east2.add(descLabel);
			// east2.add(stepLabel);
			// east.add(east2);

			JPanel east2a = new JPanel(new BorderLayout());
			east2a.setPreferredSize(new Dimension(175, 250));
			east2a.setBorder(BorderFactory.createTitledBorder("Mikrokod"));
			JPanel up = new JPanel(new GridLayout(5, 1));
			JLabel infoInstr = new JLabel("Instrukcija:");
			infoInstr.setAlignmentX(LEFT_ALIGNMENT);
			up.add(infoInstr);
			up.add(instrLabel);
			JLabel infoFaza = new JLabel("Faza izvrsavanja:");
			infoFaza.setAlignmentX(LEFT_ALIGNMENT);
			up.add(infoFaza);
			up.add(descLabel);
			JLabel infoTok = new JLabel("Tok izvrsavanja:");
			infoTok.setAlignmentX(LEFT_ALIGNMENT);
			up.add(infoTok);
			east2a.add("North", up);
			stepsArea.setText(stepLoader.allSteps());
			stepsArea.setEditable(false);
			scrollSteps = new JScrollPane(stepsArea);
			east2a.add("Center", scrollSteps);
			east.add(east2a);

			// izbacen deo za ucitavanje asemblera kada je dodat program za
			// ucitavanje asemblera
			// JPanel east2b = new JPanel(new GridLayout(1, 1));
			// east2b.setPreferredSize(new Dimension(175, 250));
			// east2b.setBorder(BorderFactory.createTitledBorder("Ucitan program"));
			// asmText = new JTextPane();
			// asmText.setText("Mesto za asm kod");
			// asmText.setEditable(false);
			// east2b.add(asmText);
			// JScrollPane scrollAsm = new JScrollPane(east2b);
			// east.add(scrollAsm);

			// panel sa dugmadima za kontrole simulacije
			JPanel east3 = new JPanel();
			east3.setLayout(new GridLayout(5, 1, 5, 5));
			east3.setBorder(BorderFactory.createTitledBorder("Kontrole"));
			JPanel CLKpanel = new JPanel(new GridLayout(1, 2, 4, 4));
			CLKpanel.add(clk_minus);
			CLKpanel.add(clk);
			east3.add(CLKpanel);
			JPanel INSpanel = new JPanel(new GridLayout(1, 2, 4, 4));
			INSpanel.add(ins_minus);
			INSpanel.add(ins);
			east3.add(INSpanel);
			east3.add(prg);
			JPanel GOTOpanel = new JPanel(new GridLayout(1, 2, 4, 4));
			GOTOpanel.add(gotob);
			GOTOpanel.add(gotot);
			east3.add(GOTOpanel);
			// JPanel RESETpanel = new JPanel(new GridLayout(1, 2, 4, 4));
			// RESETpanel.add(reset);
			// RESETpanel.add(resetnomem);
			// east3.add(RESETpanel);
			// east3.add(reset);
			JButton resetnomem = new JButton("Reset");// dugme za resetovanje
														// sekvencijalnih mreza
														// procesora ALI ne i
														// resetovanje memorije
			resetnomem.setAlignmentX(CENTER_ALIGNMENT);
			east3.add(resetnomem);
			east.add(east3);

			// izbaceno kada je dodat meni za pregled sadrzaja
			// memorije,registara,vremenskog dijagrama
			// JPanel east4 = new JPanel();
			// east4.setLayout(new GridLayout(3, 1, 5, 5));
			// east4.setBorder(BorderFactory.createTitledBorder("Pregled"));
			// east4.add(memory);
			// east4.add(registers);
			// east4.add(buttTimeDiagram);
			// east.add(east4);

			add("East", east);

			// MAIN FRAME
			setLabels();// azuriranje svih labela za prikaz(uglavnom status deo
						// istocnog panela)
			validate();
			setSize(new Dimension(scrollPane.getWidth() + west.getWidth()
					+ east.getWidth() + 400, scrollPane.getHeight() + 100));// postavljanje
																			// velicine
																			// gravnog
																			// prozora
																			// za
																			// p[rikaz
																			// simulacije
			// setBounds(0, 0,
			// sp.getWidth() + listOfShemes.getWidth() + GOTOpanel.getWidth()
			// / + 320, sp.getHeight() + 40);
			// setMinimumSize(new Dimension(800, 600));
			setMinimumSize(new Dimension(600, scrollPane.getHeight() + 100));
			setVisible(true);

			// definisanje action listenera i koje funckije da pozivaju kada se
			// pretisne neko od dugmadi
			clk_minus.addActionListener(new ActionListener() {
				@Override
				public void actionPerformed(ActionEvent arg0) {
					try {
						executeCLKminus();
					} catch (Exception e) {
						resetRunTime();
						Main.writeToLog("Error 405: Sistem je resetovan zbog nekog run time izuzetka prilikom CLK- upravljanja.");
						Main.writeStackTrace(e);
					}
				}
			});

			clk.addActionListener(new ActionListener() {

				@Override
				public void actionPerformed(ActionEvent arg0) {
					try {
						executeCLK();
					} catch (Exception e) {
						resetRunTime();
						Main.writeToLog("Error 406: Sistem je resetovan zbog nekog run time izuzetka prilikom CLK+ upravljanja.");
						Main.writeStackTrace(e);
					}

				}
			});

			ins_minus.addActionListener(new ActionListener() {

				@Override
				public void actionPerformed(ActionEvent arg0) {
					try {
						executeINSminus();
					} catch (Exception e) {
						resetRunTime();
						Main.writeToLog("Error 407: Sistem je resetovan zbog nekog run time izuzetka prilikom INS- upravljanja.");
						Main.writeStackTrace(e);
					}
				}
			});

			ins.addActionListener(new ActionListener() {

				@Override
				public void actionPerformed(ActionEvent arg0) {
					try {
						executeINS();
					} catch (Exception e) {
						resetRunTime();
						Main.writeToLog("Error 408: Sistem je resetovan zbog nekog run time izuzetka prilikom INS+ upravljanja.");
						Main.writeStackTrace(e);
					}
				}
			});

			prg.addActionListener(new ActionListener() {

				@Override
				public void actionPerformed(ActionEvent arg0) {
					try {
						executePRG();
					} catch (Exception e) {
						resetRunTime();
						Main.writeToLog("Error 409: Sistem je resetovan zbog nekog run time izuzetka prilikom PRG+ upravljanja.");
						Main.writeStackTrace(e);
					}
				}
			});

			gotob.addActionListener(new ActionListener() {

				@Override
				public void actionPerformed(ActionEvent arg0) {
					try {
						executeGOTO();
					} catch (Exception e) {
						resetRunTime();
						Main.writeToLog("Error 410: Sistem je resetovan zbog nekog run time izuzetka prilikom GOTO upravljanja.");
						Main.writeStackTrace(e);
					}

				}
			});

			// ne koristi se od trenutka kada je donesena odluka da postoji samo
			// reset procesora a ne i reset memorije
			// reset.addActionListener(new ActionListener() {

			// @Override
			// public void actionPerformed(ActionEvent arg0) {
			// reset();
			// }
			// });

			resetnomem.addActionListener(new ActionListener() {

				@Override
				public void actionPerformed(ActionEvent arg0) {
					try {
						resetnomem();
					} catch (Exception e) {
						resetRunTime();
						Main.writeToLog("Error 411: Sistem je resetovan zbog nekog run time izuzetka prilikom RESET upravljanja.");
						Main.writeStackTrace(e);
					}
				}
			});

			// dugme je izgubilo znacaj kada je napravljen meni
			// loadAsm.addActionListener(new ActionListener() {

			// @Override
			// public void actionPerformed(ActionEvent arg0) {
			// loadMem();
			// }
			// });

			// dugme je izgubilo znacaj kada je napravljen meni
			// save.addActionListener(new ActionListener() {

			// @Override
			// public void actionPerformed(ActionEvent arg0) {
			// saveSimulation();
			// }
			// });

			// dugme je izgubilo znacaj kada je napravljen meni
			// loadSim.addActionListener(new ActionListener() {

			// @Override
			// public void actionPerformed(ActionEvent arg0) {
			// loadSimulation();
			// }
			// });

			// dugme koje je izgubilo znacaj kada je napravljen meni
			// memory.addActionListener(new ActionListener() {

			// @Override
			// public void actionPerformed(ActionEvent arg0) {
			// memWindow.updateMemoryView();
			// memWindow.setVisible(true);
			// }
			// });

			// dugme koje je izgubilo znacaj kada je napravljen meni
			// registers.addActionListener(new ActionListener() {

			// @Override
			// public void actionPerformed(ActionEvent arg0) {
			// regsWindow.updateRegistersView();
			// regsWindow.setVisible(true);
			// }
			// });

			// dugme koje je izgubilo znacaj kada je napravljen meni
			// buttTimeDiagram.addActionListener(new ActionListener() {

			// @Override
			// public void actionPerformed(ActionEvent arg0) {
			// timeDiagram.updateTimeDiagramView();
			// timeDiagram.setVisible(true);
			// }
			// });

			addWindowListener(new WindowAdapter() {

				@Override
				public void windowClosing(WindowEvent e) {
					if (log != null) {
						log.close();
					}
					memWindow.dispose();
					regsWindow.dispose();
					timeDiagram.dispose();
					asmWindow.dispose();
					if(simulationFrame!=null)simulationFrame.dispose();
				}
			});

		} catch (Exception e) {
			resetRunTime();
			Main.writeToLog("Error 404: Sistem je resetovan zbog nekog run time izuzetka u Main klasi .");
			Main.writeStackTrace(e);
		}
	}

	// funkcija za azuriranje statusnog dela(ispis globalnog kloka,vrednost
	// registra PC,registra mPC)
	// i mikrokoda(instrukcija se dobije pomocu disasemblera gde pokazuje
	// PC,tako sto kada je je mikroprogram na pocetku,procita se
	// 1 bajt i ako deasembler daje dobru instrukciju onda je ok,ako ne
	// deasemblira onda se citaju dva uzastopna bajta da li daju instrukciju i
	// tako
	// sve do 4 jer instrukcija moze biti sastavljena iz najvise 4 bajta zatim
	// descLabel je labela za opis faze instrukcije)
	// azurira i obelezavanjetekuce instrukcije mikroprograma
	public void setLabels() {
		clockLabel.setText("CLK = " + LogicalComponent.globalClock);
		int step = mpc.getIntVal();
		mpcLabel.setText("mPC = " + Integer.toHexString(step));
		pcLabel.setText("PC =" + Integer.toHexString(pc.getIntVal()));
		if (step == 0) {// deo za dekodiranje instrukcije
			Disassembler d;
			int byte0 = Mem1.readMEM(pc.getIntVal());
			int byte1 = Mem1.readMEM(pc.getIntVal() + 1);
			int byte2 = Mem1.readMEM(pc.getIntVal() + 2);
			int byte3 = Mem1.readMEM(pc.getIntVal() + 3);
			ArrayList<Integer> instr=new ArrayList<Integer>();
			instr.add(byte0);
			d = new Disassembler(instr);
			if (d.isOk()) {
				instrLabel.setText(d.getDecodedInstr());
			} else {
				instr.add(byte1);
				d = new Disassembler(instr);
				if (d.isOk()) {
					instrLabel.setText(d.getDecodedInstr());
				} else {
					instr.add(byte2);
					d = new Disassembler(instr);
					if (d.isOk()) {
						instrLabel.setText(d.getDecodedInstr());
					} else {
						instr.add(byte3);
						d = new Disassembler(instr);
						if (d.isOk()) {
							instrLabel.setText(d.getDecodedInstr());
						} else {
							// doesn't exist instruction with more than four
							// bytes,but put NOP as result of disasembling
							Main.writeToLog("Error 440: Doslo je do greske u deasembliranju instrukcije tako sto nije nadjena instrukcja od 4 bajta,pa je stavljena NOP instrukcija.");
							instrLabel.setText("nop");
						}
					}
				}
			}
		}
		descLabel
				.setText(desc[step] == null ? descLabel.getText() : desc[step]);

		// ovaj deo gubi znacaj kad je dodat hiGhLighter i kada se ispisuju svi
		// step-ovi a ne samo 1 step
		// String s1 = steps[step] == null ? stepLabel.getText() : steps[step];
		// s1 = "<html>" + s1 + "</html>";
		// stepLabel.setText(s1);

		// azuriranje da svetli tekuca instrukcija mirkoprograma
		Highlighter h = stepsArea.getHighlighter();
		h.removeAllHighlights();
		try {
			h.addHighlight(stepsArea.getLineStartOffset(step),
					stepsArea.getLineEndOffset(step),
					DefaultHighlighter.DefaultPainter);
			// scrollSteps.getVerticalScrollBar().setValue(step*scrollSteps.getHeight()/240);
			scrollSteps.getViewport().setViewPosition(
					new Point(0, -9 + step * stepsArea.getHeight() / 240));
		} catch (Exception e) {
		}
	}

	// funckija za proveru da li je doslo do kraja programa sto znaci da dugmad
	// za simuliranje unapred treba da se ugase
	private void checkEnd() {
		if (start.getBoolVal() == false) {
			clk.setEnabled(false);
			ins.setEnabled(false);
			prg.setEnabled(false);
			gotob.setEnabled(false);
		}
	}

	// postavljanje dugmadi da budu aktivna
	private void enableButtons() {
		clk.setEnabled(true);
		ins.setEnabled(true);
		prg.setEnabled(true);
		gotob.setEnabled(true);
	}

	// resetovanje simulacije kada se resetuju registri i memorija je izgubilo
	// znacaj kada se donela odluka da pod resetom se podrazumeva samo reset
	// registara
	// private void reset() {
	// TimeDiagram.resetAllHistory();// bitno je da bude pre initialise jer se u
	// initialise vec pamti prvo stanje sistema
	// LogicalComponent.initMemory = true;
	// LogicalComponent.initialise();
	// setLabels();
	// enableButtons();
	// updateView();
	// JOptionPane.showMessageDialog(Main.this, "Simulacija resetovana.",
	// "Resetovanje simulacije", JOptionPane.INFORMATION_MESSAGE);
	// }

	// resetovanje simulacije tj.registara procesora
	private void resetnomem() {
		Main.writeToLog("Info: Klok " + LogicalComponent.globalClock
				+ ": Reset CPU");
		instructionsBeginings.clear();
		TimeDiagram.resetAllHistory();// bitno je da bude pre initialise jer se
										// u initialise vec pamti prvo stanje
										// sistema
		criticalMoments.clear();
		LogicalComponent.restoreSimulationState(0);//da bi se pobrisala nepotrebna stanja tokom simulacije
		
		LogicalComponent.initMemory = false;
		LogicalComponent.initialise();
		checkInstructionBegining();
		setLabels();
		enableButtons();
		updateView();
		JOptionPane.showMessageDialog(Main.this,
				"Simulator resetovan, memorija nepromenjena.",
				"Resetovanje simulacije", JOptionPane.INFORMATION_MESSAGE);
	}

	// funckija koja se poziva ako se desi neki run time reset
	private void resetRunTime() {
		instructionsBeginings.clear();
		TimeDiagram.resetAllHistory();// bitno je da bude pre initialise jer se
										// u initialise vec pamti prvo stanje
										// sistema
		criticalMoments.clear();
		LogicalComponent.restoreSimulationState(0);
		
		LogicalComponent.initMemory = false;
		LogicalComponent.initialise();
		checkInstructionBegining();
		setLabels();
		enableButtons();
		updateView();
		JOptionPane
				.showMessageDialog(
						Main.this,
						"Simulator se sam resetovao jer se desila neka od nepredvidjenih radnji(Haha imas BUG).",
						"Resetovanje simulatora",
						JOptionPane.INFORMATION_MESSAGE);
	}

	// funkcija se ne koristi od trenutka kada dodat meni sa prozorom za
	// asembler a koristila se za ucitavanje asemblerskog programa
	// private void loadMem() {
	// JFileChooser chooser = new JFileChooser("tests");
	// int returnVal = chooser.showOpenDialog(Main.this);
	// if (returnVal == JFileChooser.APPROVE_OPTION) {
	// setTitle(chooser.getSelectedFile().getName());
	// String asmcode = chooser.getSelectedFile().getAbsolutePath();
	// Assembler asm = new Assembler(asmcode, null);
	// if (!asm.isOk())
	// return;
	// asmText.setText(asm.getAsmText().toString());
	// int adr = asm.getStartOfCode();
	// for (Object o : asm.getCode()) {
	// int data = (Integer) o;
	// Mem1.getMEM().write(adr++, data);
	// }
	// REG pc = Fetch1.RegPC();
	// pc.initVal(asm.getStartOfCode());

	// LogicalComponent.initMemory = false;////////OVO SE RAZLIKUJE DA
	// NEMA KAO STO IMA KOD DVOZ
	// LogicalComponent.initialise();
	// test
	// instructionsBeginings.add(0);
	// ///////////
	// ///////////enableButtons();

	// setLabels();
	// updateView();

	// JOptionPane.showMessageDialog(Main.this,
	// "Program ucitan u memoriju", "Ucitavanje programa",
	// JOptionPane.INFORMATION_MESSAGE);
	// }
	// }

	public void updateView() {
		currentScheme.repaint();
		if (memWindow.isVisible()) {
			memWindow.updateMemoryView();
		}
		if (regsWindow.isVisible()) {
			regsWindow.updateRegistersView();
		}
		if (timeDiagram.isVisible()) {
			timeDiagram.updateTimeDiagramView();
		}
		if (asmWindow.isVisible()) {
			// nema updata funkcija jer nista ne zavisi u asembler programu
			// i ne mora da se menja kada se menja nesto na glavnom
			// prozoru,registrima ili memoriji
		}
	}

	// funkcija koja se izvrsi kada se pokrene simulacija 1 takt unazad
	private void executeCLKminus() {
		Main.writeToLog("Info: Klok " + LogicalComponent.globalClock + ": CLK-");
		if (LogicalComponent.globalClock == 0)
			return;// ignorise se kada je klok na vrednosti 0 jer ne moze ici u
					// minus klok

		if(instructionsBeginings.getLast()==(LogicalComponent.globalClock)){
			instructionsBeginings.removeLast();//da se odsece pocetak instrukcija
		}
		
		// restaurira se stanje sekvencijalnih komponenti iz tog zeljenog kloka
		LogicalComponent.restoreSimulationState(LogicalComponent.globalClock - 1);

		// za sve signale koji se iscrtavaju mora da se izbaci iz njihove
		// istorije po jedna(zadnja) vrednost zbog ove akcije
		TimeDiagram.removeEndOfHistory();
		setLabels();
		updateView();
	}

	private void executeCLK() {
		Main.writeToLog("Info: Klok " + LogicalComponent.globalClock + ": CLK+");
		checkEnd();
		LogicalComponent.CLK();
		checkInstructionBegining();
		checkEnd();
		setLabels();
		updateView();
	}

	public void executeINSminus() {
		Main.writeToLog("Info: Klok " + LogicalComponent.globalClock + ": INS-");
		if (LogicalComponent.globalClock == 0
				|| instructionsBeginings.isEmpty())
			return;
		Integer lastINS = instructionsBeginings.removeLast();

		// u slucaju da smo bas u kloku kada pocinje nova instrukcija(mPC=0)
		// tada poslednji element u instructionsBeginings
		// je trenutni klok i njega treba da ignorisemo jer INS- nema znacaja
		// tada,zato se uzme jos 1 poslednja vrednost iz liste klokova gde
		// pocinju instrukcije
		if (LogicalComponent.globalClock == lastINS) {
			if (instructionsBeginings.isEmpty()) {
				throw new RuntimeException();// baci se izuzetak da je zakazalo
												// brojanje pocetaka instrukcija
			}
			lastINS = instructionsBeginings.removeLast();
		}
		// restaurira se stanje sekvencijalnih komponenti iz tog zeljenog kloka
		LogicalComponent.restoreSimulationState(lastINS);

		// za sve signale koji se iscrtavaju mora da se izbaci iz njihove
		// istorije po jedna(zadnja) vrednost zbog ove akcije
		TimeDiagram.removeEndOfHistory();

		checkInstructionBegining();
		setLabels();
		updateView();
	}

	// provera da li instrukcija pocinje od nekog kloka jer taj klok mora da se
	// zapamti kasnije kada se idu instrukcije unazad da bi se vratilo na tu
	// instrukciju
	private void checkInstructionBegining() {
		if (mpc.getIntVal() == 0) {
			if(!instructionsBeginings.contains(LogicalComponent.globalClock))
					instructionsBeginings.add(LogicalComponent.globalClock);
		}
	}

	// funkcija koja se poziva kada se pokrene simulacija 1 instrukciju unapred
	private void executeINS() {
		Main.writeToLog("Info: Klok " + LogicalComponent.globalClock + ": INS+");
		checkEnd();

		LogicalComponent.CLK();
		checkEnd();
		while ((mpc.getIntVal() != 0) && (start.getBoolVal() == true)) {
			LogicalComponent.CLK();
		}
		checkInstructionBegining();
		checkEnd();
		setLabels();
		updateView();
	}

	// funkcija koja se poziva kada se pokrene izvrsavanje programa do kraja
	private void executePRG() {
		Main.writeToLog("Info: Klok " + LogicalComponent.globalClock + ": PRG+");
		checkEnd();
		while (start.getBoolVal() == true) {
			LogicalComponent.CLK();
			checkInstructionBegining();
		}
		setLabels();
		checkEnd();
		updateView();
	}

	// funkcija koja se poziva kada se zeli skok na odredjen klok
	private void executeGOTO() {
		Main.writeToLog("Info: Klok " + LogicalComponent.globalClock
				+ ": GOTO " + gotot.getText());

		String s = gotot.getText();//uzme se unet string
		int n = -1;
		try {
			n = Integer.parseInt(s);//baci se izuzetak ako je lose uneto nesto sto nije broj
			if (n == -1 || n<0) {//ako je n manje od nule onda se samo izadje
				gotot.setText("");
				return;
			}
			if (n == 0) {
				// samo reset sistema bez memorije
				resetnomem();
				
			} else if (n < LogicalComponent.globalClock) {
				int numReturns = n-LogicalComponent.globalClock;//koliko klokova treba da se vrati unazad
				//ako se ide unazad da se izbace poceci instrukcija iz liste
				for(int i=0;i<numReturns;i++){
					if(instructionsBeginings.getLast()==(LogicalComponent.globalClock-i)){
						instructionsBeginings.removeLast();//da se odseku poceci instrukcija
					}
				}
				LogicalComponent.restoreSimulationState(n);
				TimeDiagram.removeEndOfHistory();//odsecanje istorije za prikaz vrednosti signala na dijagramima
				
			} else if (n > LogicalComponent.globalClock) {
				while (Exec2.START().getBoolVal() == true
						&& LogicalComponent.globalClock < n) {
					LogicalComponent.CLK();
					checkInstructionBegining();//nasao se pocetak instrukcije pa se ide unapred
				}
			} else {
				// nista ne radi jer je u istom kloku na koji treba da skoci sa goto
			}
			gotot.setText("");
			setLabels();
			checkEnd();
			updateView();

		} catch (NumberFormatException e) {
			//ako se ne unese integer broj
			gotot.setText("");
			JOptionPane.showMessageDialog(Main.this,
					"Morate uneti broj (dec) kloka na koji se zeli skociti dugmetom GOTO.",
					"GOTO kontrola", JOptionPane.INFORMATION_MESSAGE);
			return;
		}
	}
	
	//pamte se klokovi kada je menjano rucnim unosom nesto u registre ili memoriju
	public void saveCriticalMoment() {
		Integer currCLK = LogicalComponent.globalClock;
		if(!criticalMoments.contains(currCLK)) {
			criticalMoments.add(currCLK);
		}
	}
	

	// funkcija koja je reakcija na dugme za snimanje simualacije iz menija
	private void saveSimulation() {
		Main.writeToLog("Info: Klok " + LogicalComponent.globalClock
				+ ": Save simulation ");
		try {
			JFileChooser chooser = new JFileChooser("Save simulation state");
			chooser.setCurrentDirectory(new File("./save/dmmm"));
			int returnVal = chooser.showSaveDialog(this);
			if (returnVal == JFileChooser.APPROVE_OPTION) {
				PrintWriter writer = new PrintWriter(new FileWriter(
						chooser.getSelectedFile().getPath()), true);
				LogicalComponent.writeSimulation(criticalMoments,writer);
				writer.close();
				JOptionPane.showMessageDialog(Main.this,
						"Uspesno je snimljeno stanje simulacije.",
						"Snimanje simulacije",
						JOptionPane.INFORMATION_MESSAGE);
			}
		} catch (IOException e) {
			Main.writeToLog("Error 420: Error opening save file!");
		}
		updateView();
	}

	// funkcija koja je reakcija za ucitavanje stanja simulacije iz menija
	private void loadSimulation() {
		Main.writeToLog("Info: Klok " + LogicalComponent.globalClock
				+ ": Load simulation");
		try {
			JFileChooser chooser = new JFileChooser("Save simulation state");
			chooser.setCurrentDirectory(new File("./save/dmmm"));
			int returnVal = chooser.showOpenDialog(this);
			if (returnVal == JFileChooser.APPROVE_OPTION) {
				BufferedReader reader = new BufferedReader(new FileReader(
						chooser.getSelectedFile().getPath()));

				int numOfStates = Integer.parseInt(reader.readLine());

				TimeDiagram.resetAllHistory();//brisu se sva stanja iz istorije za iscrtavanje,brisu se svi poceci instrukcija
				instructionsBeginings.clear();//sada su novi poceci instrukcija kada je ucitana nova simulacija
				criticalMoments.clear();//jer sada su kriticni momenti drugaciju
				
				LogicalComponent.initMemory = false;
				LogicalComponent.initialise();

				
				LogicalComponent.globalClock = Integer.parseInt(reader.readLine());
				LogicalComponent.loadSimulationState(reader);
				checkInstructionBegining();
				saveCriticalMoment();
				
				for(int i = 1; i < numOfStates; i++) {
					int newClock = Integer.parseInt(reader.readLine());
					for(int j = LogicalComponent.globalClock; j < newClock; j++) {
						LogicalComponent.CLK();
						checkInstructionBegining();
					}
					LogicalComponent.loadSimulationState(reader);
					saveCriticalMoment();
				}
				reader.close();
				
				checkInstructionBegining();
				checkEnd();
				setLabels();
				updateView();

				JOptionPane.showMessageDialog(Main.this,
						"Uspesno je ucitana simulacija.",
						"Ucitavanje simulacije",
						JOptionPane.INFORMATION_MESSAGE);
			}
		} catch (FileNotFoundException e) {
			Main.writeToLog("Error 421: Save file not found!");
			Main.writeStackTrace(e);
		} catch (IOException e) {
			Main.writeToLog("Error 422: Error closing file!");
			Main.writeStackTrace(e);
		}
		updateView();
	}

	// instanciranje klasa na svim semama koje simbolizuju logicka kola
	private void initAll() {
		Sistem.init();
		Cpu.init();
		Mem.init();
		Adr1.init();
		Bus1.init();
		Counter.init();
		Exec1.init();
		Exec2.init();
		Exec3.init();
		Exec4.init();
		Fetch1.init();
		Fetch2.init();
		Fetch3.init();
		Intr1.init();
		Intr2.init();
		Intr3.init();
		Mem1.init();
		Mem2.init();
		Oper1.init();
		Uprav1.init();
	}

	// povezivanje objekata koji predstavljaju logicka kola na semama
	private void connectAll() {
		Sistem.connect();
		Cpu.connect();
		Mem.connect();
		Adr1.connect();
		Bus1.connect();
		Counter.connect();
		Exec1.connect();
		Exec2.connect();
		Exec3.connect();
		Exec4.connect();
		Fetch1.connect();
		Fetch2.connect();
		Fetch3.connect();
		Intr1.connect();
		Intr2.connect();
		Intr3.connect();
		Mem1.connect();
		Mem2.connect();
		Oper1.connect();
		Uprav1.connect();
	}

	// iscrtavanje grafickih komponenata na semama i njihovo vezivanje za pinove
	// od kojih dobijaju vrednosti
	private void guiAll() {
		Sistem.gui();
		Cpu.gui();
		Mem.gui();
		Adr1.gui();
		Bus1.gui();
		Counter.gui();
		Exec1.gui();
		Exec2.gui();
		Exec3.gui();
		Exec4.gui();
		Fetch1.gui();
		Fetch2.gui();
		Fetch3.gui();
		Intr1.gui();
		Intr2.gui();
		Intr3.gui();
		Mem1.gui();
		Mem2.gui();
		Oper1.gui();
		Uprav1.gui();

		// stavi se u mem11
		// Mem1.writeMEM(0, 192);
		// Debuger d=new Debuger();
	}

	public GuiScheme getCurrentScheme() {
		return currentScheme;
	}

	// ovu funkciju koristi klasa Menu koja predstavlja navigator
	public void setCurrentScheme(GuiScheme g) {
		scrollPane.remove(currentScheme);
		currentScheme = g;
		scrollPane.add(currentScheme);
		// repaint(); poziva se u validate
		validate();
	}

	// pravljenje menija
	private void makeMenuBar() {

		JMenuBar menuBar = new JMenuBar();
		JMenu file = new JMenu();
		JMenuItem file1 = new JMenuItem();
		JMenuItem file2 = new JMenuItem();
		JMenuItem file3 = new JMenuItem();
		JMenu view = new JMenu();
		JMenuItem view1 = new JMenuItem();
		JMenuItem view2 = new JMenuItem();
		JMenuItem view3 = new JMenuItem();
		JMenuItem view4 = new JMenuItem();
		JMenu help = new JMenu();
		JMenuItem help1 = new JMenuItem();

		file.setText("File");

		file1.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_L,
				InputEvent.CTRL_MASK));
		file1.setText("Load Sim");
		file1.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent evt) {
				loadSimulation();
			}
		});
		file.add(file1);

		file2.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_S,
				InputEvent.CTRL_MASK));
		file2.setText("Save Sim");
		file2.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent evt) {
				saveSimulation();
			}
		});
		file.add(file2);

		file3.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F4,
				InputEvent.ALT_MASK));
		file3.setText("Exit");
		file3.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent evt) {
				if (log != null) {
					log.close();
				}
				timeDiagram.dispose();
				regsWindow.dispose();
				memWindow.dispose();
				if (simulationFrame != null)
					simulationFrame.dispose();
				dispose();
				dispatchEvent(new WindowEvent(Main.this,
						WindowEvent.WINDOW_CLOSING));

				// =System.exit(0);
			}
		});
		file.add(file3);

		menuBar.add(file);

		view.setText("View");
		view1.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_A,
				InputEvent.CTRL_MASK));
		view1.setText("Assemly");
		view1.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent evt) {
				asmWindow.resetLabels();
				asmWindow.setVisible(true);
			}
		});
		view.add(view1);

		view2.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_R,
				InputEvent.CTRL_MASK));
		view2.setText("Registers");
		view2.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent evt) {
				regsWindow.updateRegistersView();
				regsWindow.setVisible(true);
			}
		});
		view.add(view2);

		view3.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_M,
				InputEvent.CTRL_MASK));
		view3.setText("Memory");
		view3.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent evt) {
				memWindow.updateMemoryView();
				memWindow.setVisible(true);
			}
		});
		view.add(view3);

		view4.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_T,
				InputEvent.CTRL_MASK));
		view4.setText("Time Diagram");
		view4.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent evt) {
				timeDiagram.updateTimeDiagramView();
				timeDiagram.setVisible(true);
			}
		});
		view.add(view4);

		menuBar.add(view);

		help.setText("Help");
		help1.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_F,
				InputEvent.CTRL_MASK));
		help1.setText("About");
		help1.addActionListener(new ActionListener() {
			public void actionPerformed(ActionEvent evt) {
				JDialog dialog = new JDialog();
				dialog.setMinimumSize(new Dimension(350, 250));
				dialog.getContentPane().setLayout(new GridLayout(5, 1));
				JLabel l1 = new JLabel();
				l1.setFont(new Font("Times New Roman", 1, 16));
				l1.setHorizontalAlignment(SwingConstants.CENTER);
				l1.setText("Računarski sistem za rad u laboratoriji");
				dialog.add(l1);
				JLabel l2 = new JLabel();
				l2.setFont(new Font("Times New Roman", 0, 14));
				l2.setHorizontalAlignment(SwingConstants.CENTER);
				l2.setText("Verzija: 1.0");
				dialog.add(l2);
				JLabel l3 = new JLabel();
				l3.setFont(new Font("Times New Roman", 0, 14));
				l3.setHorizontalAlignment(SwingConstants.CENTER);
				l3.setText("Autori:");
				dialog.add(l3);
				JLabel l4 = new JLabel();
				l4.setFont(new Font("Times New Roman", 0, 14));
				l4.setHorizontalAlignment(SwingConstants.CENTER);
				l4.setText("Prof.Dr Jovan Đorđević");
				dialog.add(l4);
				JLabel l5 = new JLabel();
				l5.setFont(new Font("Times New Roman", 0, 14));
				l5.setHorizontalAlignment(SwingConstants.CENTER);
				l5.setText("Dipl. inž. Darko Tamburić");
				dialog.add(l5);

				dialog.setVisible(true);
			}
		});
		help.add(help1);

		menuBar.add(help);

		setJMenuBar(menuBar);
	}

	public static void main(String[] args) {

		long begin = System.currentTimeMillis();
		new Main(null);
		Main.writeToLog("Info: startup: "
				+ (System.currentTimeMillis() - begin) + " ms");
	}

	public static void writeToLog(String s) {
		System.out.println(s);
		if (log != null)
			log.println(s);
	}

	public static void writeStackTrace(Exception e) {
		e.printStackTrace();
		if (log != null)
			e.printStackTrace(log);
	}
}
